class Characteristic {
	constructor(conn, devid, serid) {
		this.conn = conn;
		this.devid = devid;
		this.serid = serid + "";
	}

	zero(str, len) {
		var zero = "00000000";
		zero = zero + str;
		return zero.substring(zero.length - 2);
	}

	checkCRC(str) {
		var bCheck = 0;
		for (var i = 0; i < str.length; i++) {
			bCheck = bCheck ^ str.charCodeAt(i);
		}
		return this.zero(bCheck.toString(16), 2);
	}

	writeCha(devid, serid, cha, ord) {
		var buf = new ArrayBuffer(ord.length);
		var bufView = new Uint8Array(buf);
		for (var i = 0; i < ord.length; i++) {
			bufView[i] = ord.charCodeAt(i);
		}
		// console.log("buffer:", bufView[0], bufView[1], bufView[2], bufView[11], bufView[12], bufView[13], bufView[14], bufView[15]);
		// console.log("writecha:", this.conn.writecha);

		wx.writeBLECharacteristicValue(
			{
				deviceId: devid,
				serviceId: serid,
				characteristicId: cha,
				value: bufView.buffer,
				success: res => {
					// success
					// console.log("success  first 指令发送成功:", cha);
					// console.log(res);
				},
				fail: res => {
					// fail
					// console.log("write failed:", res);
					this.conn.opendoor.StopOpenProcess("wblech failed",false,this.conn);
				},
				complete: function (res) {
					// complete
				}
			})
	}

	Doit() {
		wx.getBLEDeviceCharacteristics(
			{
				// 这里的 deviceId 需要在上面的 getBluetoothDevices
				deviceId: this.devid,
				// 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
				serviceId: this.serid,
				success: cha => {
					for (var j = 0; j < cha.characteristics.length; j++) {
						if (cha.characteristics[j].properties.notify) {
							// console.log("bind notify", cha.characteristics[j])
							this.conn.notifycha = cha.characteristics[j].uuid + "";
							this.conn.notifyser = this.serid;

							wx.onBLECharacteristicValueChange(ch => {
								// console.log('characteristic value comed:', ch.value);

								var reqCMD = String.fromCharCode.apply(null, new Uint8Array(ch.value));
								// console.log('recv:', reqCMD);
								if (reqCMD.indexOf("@03") >= 0) {
									this.conn.CloseConnect(true);
									this.conn.opendoor.callback({ code: 200, info: "开门成功", title: "", content: "" });
									console.log("回复退出." + reqCMD);
									this.conn.opendoor.StopOpenProcess("open success", false, this.conn);
								} else if (reqCMD.indexOf("@ER") >= 0) {
									this.conn.CloseConnect(false);
									this.conn.opendoor.callback({ code: 503, info: "开门失败", title: "", content: "" });
									console.log("回复错误." + reqCMD);
									this.conn.opendoor.StopOpenProcess("open error", false, this.conn);
								} else if (reqCMD.indexOf("@04") >= 0) {
									var open_key = this.conn.openkey;
									var TID = open_key.substring(3, 11);
									var KEY = reqCMD.substring(5, 9);
									var sKey = parseInt(KEY, 16);
									var sNum = sKey % 16;

									sKey = ((sKey << sNum) | (sKey >> (16 - sNum)));
									var KEYS = new Uint8Array(2);
									KEYS[0] = sKey / 256;
									KEYS[1] = sKey % 256;
									var TIDS = new Uint8Array(4);
									//test
									//comment
									TIDS[0] = parseInt(TID.substring(0, 2), 16);
									TIDS[1] = parseInt(TID.substring(2, 4), 16);
									TIDS[2] = parseInt(TID.substring(4, 6), 16);
									TIDS[3] = parseInt(TID.substring(6, 8), 16);
									for (var i = 0; i < 4; i++) {
										TIDS[i] = TIDS[i] ^ KEYS[i % 2];
									}
									var mCmd = open_key.replace(TID, this.zero(TIDS[0].toString(16), 2) + this.zero(TIDS[1].toString(16), 2) + this.zero(TIDS[2].toString(16), 2) + this.zero(TIDS[3].toString(16), 2));
									mCmd = mCmd.substring(0, mCmd.length - 3) + this.checkCRC(mCmd.substring(1, mCmd.length - 3)) + "*";
									console.log("发出开门指令." + mCmd);
									this.writeCha(this.conn.devid, this.conn.writeser, this.conn.writecha, mCmd);
								}
							})
							/**
							 * 顺序开发设备特征notifiy
							 */
							wx.notifyBLECharacteristicValueChanged({
								deviceId: this.devid,
								serviceId: this.serid,
								characteristicId: this.conn.notifycha,
								state: true,
								success: function (res) {
									// success
									// console.log('notifyBLECharacteristicValueChanged success', res);
								},
								fail: res=> {
									// fail
									// console.log('notifyBLECharacteristicValueChanged fail', res);
									this.conn.opendoor.StopOpenProcess("notify ch change failed", false, this.conn);
								},
								complete: function (res) {
									// complete
								}
							})
						}

						if (cha.characteristics[j].properties.write) {
							// console.log("bind write", cha.characteristics[j])
							if (this.conn.writeser == "") {
								this.conn.writecha = cha.characteristics[j].uuid + "";
								this.conn.writeser = this.serid;

								var ord = "@04000000001603*";
								this.writeCha(this.conn.devid, this.conn.writeser, this.conn.writecha, ord);
							}
						}
					}
				},
				fail: res=> {
					// console.log(res);
					this.conn.opendoor.StopOpenProcess("get ble ch failed", false, this.conn);
				}
			})
	}
}

class Connection {
	constructor(opendoor, devid, openkey) {
		this.devid = devid;
		this.notifycha = "";
		this.writecha = "";
		this.notifyser = "";
		this.writeser = "";
		this.opendoor = opendoor;
		this.openkey = openkey;

		this.opensuccess = false;
		this.ctrytimes = 0;
		this.isclose = false;
		this.isconnected = false;
	}

	CloseConnect(opensuccess) {
		this.opensuccess = opensuccess;

		wx.closeBLEConnection(
			{
				deviceId: this.devid,
				success: function (res) { },
				complete: res => {
					this.opendoor.once = true;
					
					this.opendoor.StopOpenProcess("close connect", false, null);
				}
			})
	}

	TryIn()
	{
		if (!this.opensuccess && this.ctrytimes < this.opendoor.times && !this.isclose) {
			this.ctrytimes++;

			wx.createBLEConnection(
				{
					deviceId: this.devid,
					success: res => {
						this.opendoor.callback({ code: 102, info: "连接设备成功", title: "", content: "" });
						console.log("连接设备成功", res)

						setTimeout(function () {
							wx.getBLEDeviceServices(
								{
									// 这里的 deviceId 需要在上面的 getBluetoothDevices中获取
									deviceId: this.devid,
									success: service => {
										// console.log('device services:', service.services)
										for (var i = 0; i < service.services.length; i++) {
											var gc = new Characteristic(this, this.devid, service.services[i].uuid);
											gc.Doit();
										}
									},
									fail: res => {
										// fail
										// console.log('getser failed:', res)
										this.opendoor.StopOpenProcess("get ble ser failed", false, this);
									},
									complete: function (res) {
										// complete
									}
								})
						}.bind(this), 10)
					},
					fail: res => {
						this.opendoor.callback({ code: 502, info: "连接设备失败", title: "", content: "" });
						console.log("connect failed:", res)
						this.opendoor.StopOpenProcess("connect failed", false, this);
					}
				})
		} else if (!this.isclose) {
			this.isclose = true;
			setTimeout(function () { this.opendoor.StopOpenProcess("open over", true, null) }.bind(this), 500);
		}
	}

	TryN()
	{
		console.log("try...........................", this.ctrytimes+1);

		if ( this.isconnected )
			wx.closeBLEConnection({
				deviceId: this.devid,
				success: function (res) { },
				complete: res => {
					// console.log("TryN closeBLE complete");

					this.TryIn();
				}
			})
		else
			this.TryIn();

		if (this.opensuccess || this.ctrytimes >= this.opendoor.times)
		{
		}
	}

	Connect() {
		wx.onBLEConnectionStateChanged(res => {
			console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`);
			var status = "";
			if (res.connected)
			{
				this.isconnected = true;
				this.opendoor.callback({ code: 150, info: "连接成功", title: "", content: "" });
			}else
			{
				this.isconnected = false;
				this.opendoor.callback({ code: 504, info: "连接中断", title: "", content: "" });
				this.opendoor.StopOpenProcess("connect break", false, null);
			}
		})

		this.opensuccess = false;
		this.ctrytimes = 0;

		setTimeout(function () {
			if (!this.isclose)
			{
				this.isclose = true;
				this.CloseConnect(false);
				this.opendoor.StopOpenProcess("open timeout", true, null);
			}
		}.bind(this),
			(this.opendoor.timeout+1666)*this.opendoor.times);

		this.TryN();
	}
}

class OpenDoor {
	constructor() {
		this.successopen = false;
		this.isbluetoothready = false;
		this.searchingstatus = false;

		this.onBLEStateChange = false;
		this.onBLEDeviceFound = false;
		this.once = true;

		this.BLEGDList = [];
		this.openning = false;
		this.intimer = 2;
		this.seed = 0;
	}

	StopScan(devid) {
		// for (var i = 0; i < this.BLEGDList.length; i++) {
		// 	if (this.BLEGDList[i].device_id == devid) {
		// 		var conn = new Connection(this, devid, this.BLEGDList[i].key);
		// 		setTimeout(function () {
		// 			console.log("开始连接蓝牙...")
		// 			conn.Connect();
		// 		}.bind(this), 10)
		// 	}
		// }
		console.log("opening,intimer:", this.openning, this.intimer);
		if ( this.intimer == 2 && !this.openning )
		{
			this.openning = true;
			this.updateAll_status(this.BLEGDList, devid, this.intimer == 2 && !this.openning,0,"");
			// console.log("!!!!!!!!!!!!!!! into Opendoor");

			wx.stopBluetoothDevicesDiscovery({
				success: res => {
					console.log("成功停止搜索:", res)
				},
				complete: res => {
					var info = "";
					if (this.BLEGDList.length == 0)
						info = "未发现，请点击搜索";
					else
						info = "点击重新搜索";
					// this.updatedis_status(false, this.intimer == 2 && !this.openning,info);
					this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, 0, "");
					for ( var i = 0; i < this.BLEGDList.length; i ++ ){
						if ( this.BLEGDList[i].device_id == devid){
							var conn = new Connection(this, devid, this.BLEGDList[i].key);
							setTimeout(function () {
								console.log("开始连接蓝牙:", res)
								conn.Connect();
							}.bind(this), 10)
						}
					}
				}
			})
		}
		// else
		// 	setTimeout(function(){this.StopScan(devid)}.bind(this),50);
	}

	StopOpenProcess(step,close,tryn)
	{
		console.log("open process:",step,close,tryn);
			if (close)
							// wx.startBluetoothDevicesDiscovery(
							// 	{
							// 		success: res => {
							// 			console.log(step, " StartBLEDiscovery OK");
							// 		},
							// 		fail: res => {
							// 			console.log(step, " StartBLEDiscovery Failed");
							// 		},
							// 		complete: res =>
									{
										setTimeout(function (){
											this.openning = false;
											this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, 0, "");
											// console.log("!!!!!!!!!!!!!!! out Opendoor");
										}.bind(this),500)
										// console.log(step, " StartBLEDiscovery");
									}
								// })
			else if ( tryn != null  )
				tryn.TryN();
	}

	StartScan(timeout, times, egklist, callback, refreshBLTBTN, updateAll_status, updatedis_status) {
		this.times = times;
		this.timeout = timeout;
		this.egklist = egklist;
		this.callback = callback;
		this.refreshBLTBTN = refreshBLTBTN;
		this.updatedis_status = updatedis_status;
		this.updateAll_status = updateAll_status;

		wx.openBluetoothAdapter(
			{
				success: res => {
//					this.callback({ code: 100, info: "初始化蓝牙成功", title: "", content: "" });

					if (!this.onBLEStateChange) {
						this.onBLEStateChange = true;

						wx.onBluetoothAdapterStateChange(res => {
							console.log("蓝牙适配器状态变化", res);
							this.isbluetoothready = res.available;
							this.searchingstatus = res.discovering;
						})
					}

					if (!this.onBLEDeviceFound) {
						this.onBLEDeviceFound = true;

						wx.onBluetoothDeviceFound(devices => {
							console.log('发现新蓝牙设备', devices);

							for (var i = 0; i < devices.devices.length; i++) {
								console.log(devices.devices[i])
								console.log('设备id' + devices.devices[i].deviceId)
								console.log('设备name' + devices.devices[i].name)

								for (var j = 0; j < this.egklist.length; j++)
									if (this.egklist[j].name == devices.devices[i].name) {
										var inlist = false;
										for(var l = 0; l < this.BLEGDList.length; l ++)
											if ( this.egklist[j].name == this.BLEGDList[l].name)
											{
												inlist = true;
												this.BLEGDList[l].timestamp = Date.parse(new Date());
												break;
											}
										if ( !inlist ){
											console.log('发现新合法蓝牙设备', devices);
											this.BLEGDList.push({rssi:devices.devices[i].RSSI,
																device_id: devices.devices[i].deviceId,
																key:this.egklist[j].key,
																name:this.egklist[j].name,
																address:this.egklist[j].address,
																timestamp: Date.parse(new Date())});
											this.refreshBLTBTN(this.BLEGDList);
										}
									}
							}
						})
					}

					wx.startBluetoothDevicesDiscovery(
						{
							success: res => {
								console.log("开始搜索附近蓝牙设备")
								// console.log(res)
							},
							complete: res => {
								// this.updatedis_status(true, this.intimer == 2 && !this.openning,"搜索蓝牙门禁中...");
								this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, 1, "搜索蓝牙门禁中...");
								setTimeout(function () {
									wx.stopBluetoothDevicesDiscovery({
										success: function (res) {
											console.log("成功停止搜索:", res)
										},
										complete: res => {
											var info = "";
											if (this.BLEGDList.length  == 0 )
												info = "未发现，点击搜索";
											else
												info = "点击重新搜索";
											// this.updatedis_status(false, this.intimer == 2 && !this.openning,info);
											this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, 0, "");
											console.log("停止搜索完成:", res)
										}
									})
								}.bind(this), timeout * 3);
							}
						})

					// this.RefreshBLTBTNTimer(timeout,true);
				},
				fail: res => {
					this.callback({ code: 501, info: "初始化蓝牙失败", title: "提示", content: "手机蓝牙未打开" });
					this.isbluetoothready = false;
					this.searchingstatus = false;
				}
			})
	}

	RefreshBLTBTNTimer(timeout,settimer)
	{
		var cseed = this.seed++;
		var ctimeout = settimer?timeout*3:10;

		setTimeout(function (){
			console.log("opening,intimer:", this.openning, this.intimer);
			if (!this.openning && this.intimer == 2) {
				var cintimer = this.intimer;
				this.intimer = 0;
				this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning,-1,"");
				// console.log("!!!!!!!!!!!!!!! into timer",cseed,cintimer);

				wx.closeBluetoothAdapter({
					success: function (res) { },
					complete: res => {
						wx.openBluetoothAdapter(
							{
								success: res => {
									wx.startBluetoothDevicesDiscovery(
										{
											success: res => {
												console.log("Timer StartBLEDiscovery OK");
											},
											fail: res => {
												console.log("Timer StartBLEDiscovery Failed");
											},
											complete: res => {
												setTimeout(function(){
													// this.updatedis_status(true, this.intimer == 2 && !this.openning,"搜索蓝牙门禁中...");
													this.intimer ++;
													this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, 1, "搜索蓝牙门禁中...");
													// console.log("!!!!!!!!!!!!!!! out timer1",cseed);
												}.bind(this),500);

												if ( !settimer )
													setTimeout(function(){
														wx.stopBluetoothDevicesDiscovery({
															success: function(res) {
																console.log("成功停止搜索:", res)
															},
															complete: res=> {
																var info = "";
																if (this.BLEGDList.length == 0)
																	info = "未发现，请点击搜索";
																else
																	info = "点击重新搜索";
																// this.updatedis_status(false, this.intimer == 2 && !this.openning,info);
																this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, 0, "");
																console.log("停止搜索完成:", res)
															}
														})
													}.bind(this),timeout*3);

												console.log("Timer StartBLEDiscovery");
											}
										})
								},
								fail: res => {
									this.intimer++;
									this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, -1, "");
								}
							})
					}
				})

				for (var i = 0; i < this.BLEGDList.length; i++) {
					var timestamp = Date.parse(new Date());

					if (this.BLEGDList[i].timestamp + timeout*5 <= timestamp) {
						// console.log("BLEGDList.length:", this.BLEGDList.length, this.BLEGDList[i].timestamp, timestamp );
						this.BLEGDList.splice(i, 1);
						i--;
						console.log("remove btn.............................",this.BLEGDList.length);
					}
				}

				this.refreshBLTBTN(this.BLEGDList);

				this.intimer ++;
				this.updateAll_status(this.BLEGDList, null, this.intimer == 2 && !this.openning, -1, "");
				// console.log("!!!!!!!!!!!!!!! out timer2",cseed);
			}
			// else if ( !settimer )
			// 	setTimeout(function () { this.RefreshBLTBTNTimer(timeout, settimer) }.bind(this), 50);

			if ( settimer )
				this.RefreshBLTBTNTimer(timeout,settimer);

			console.log("timeout:",timeout,cseed);
		}.bind(this), ctimeout);
	}
}

var g_blegd = new OpenDoor(); 
//export const g_blegd=new OpenDoor();

//export { g_blegd };
module.exports=g_blegd;